package com.cnaaa.pay.util;

import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * 2018/11/2 Create by 郭文梁
 * StringBitmapUtil
 * 将字符串--为点阵数组转换工具
 *
 * @author 郭文梁
 * @date 2018/11/2
 */
public class StringBitmapUtil {
    private static final int ONE_BYTE = 8;
    public static final int HORIZONTAL = 0x01;
    public static final int VERTICAL = 0X02;

    /**
     * 转换为位图二进制数组
     * <p>
     * 字节序：使用什么方向来产生一个字节
     * 1.横向时：左边为最高位，向右抓取8的像素生成一个字节
     * 2.纵向时：上边为最高为，向下抓取8个像素生成一个字节
     * </p>
     * <p>
     * 取码方向：取完一个字节后向那个方向继续取下一个字节
     * 1.横向时：向右取，此时返回数组中第一维度位y（纵向）
     * 2.纵向时：向下取，此时返回数组中第一维度位x（横向）
     * </p>
     *
     * @param str              字符串
     * @param fontName         字体名称
     * @param fontSize         字体大小
     * @param hSize            纵向size 为8的倍数
     * @param bold             加粗
     * @param margin           与底边距离
     * @param byteOrientation  字节序
     * @param arrayOrientation 取码方向
     * @return 二进制数组
     */
    public static short[][] toBitmap(String str,//被转换的字符串
                                     String fontName, //字体
                                     int fontSize, //字体大小
                                     int hSize,//纵向大小
                                     boolean bold,//加粗
                                     int margin,//与低端的编剧
                                     int byteOrientation,//字节序 横向或纵向
                                     int arrayOrientation//取码方向 横向或纵向
    ) {
        int height = hSize <= 0 ? 16 : hSize;
        int width = str.length() * hSize;
        Font font = new Font(fontName, bold ? Font.BOLD : Font.PLAIN, fontSize);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
        Graphics graphics = image.getGraphics();
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);
        graphics.setColor(Color.BLACK);
        graphics.setFont(font);
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            String s = new String(new char[]{c});
            graphics.drawString(s, i * hSize, height - margin);
        }

        if (arrayOrientation == HORIZONTAL) {
            return toHorizontalShortArray(image, byteOrientation);
        } else if (arrayOrientation == VERTICAL) {
            return toVerticalShortArray(image, byteOrientation);
        } else {
            throw new IllegalArgumentException("Argument orientation only can be HORIZONTAL or VERTICAL");
        }
    }

    private static short[][] toVerticalShortArray(BufferedImage image, int byteOrientation) {
        if (byteOrientation != HORIZONTAL && byteOrientation != VERTICAL) {
            throw new IllegalArgumentException("Argument orientation only can be HORIZONTAL or VERTICAL");
        }
        short[][] arr;
        if (byteOrientation == HORIZONTAL) {
            int size = image.getWidth() % ONE_BYTE == 0 ? image.getWidth() / ONE_BYTE : image.getWidth() / ONE_BYTE + 1;
            arr = new short[size][];
        } else {
            arr = new short[image.getWidth()][];
        }
        for (int y = 0; y < image.getHeight(); ) {
            for (int x = 0; x < image.getWidth(); ) {
                if (byteOrientation == HORIZONTAL) {
                    short one = toHorizontalShort(image, x, y);
                    if (arr[x / ONE_BYTE] == null) {
                        arr[x / ONE_BYTE] = new short[image.getHeight()];
                    }
                    arr[x / ONE_BYTE][y] = one;
                    x += ONE_BYTE;
                } else {
                    short one = toVerticalShort(image, x, y);
                    if (arr[x] == null) {
                        arr[x] = new short[image.getHeight() % ONE_BYTE == 0 ?
                                image.getHeight() / ONE_BYTE :
                                image.getHeight() / ONE_BYTE + 1];
                    }
                    arr[x][y / ONE_BYTE] = one;
                    x++;
                }
            }
            if (byteOrientation == HORIZONTAL) {
                y++;
            } else {
                y += ONE_BYTE;
            }
        }
        return arr;
    }

    private static short[][] toHorizontalShortArray(BufferedImage image, int byteOrientation) {
        if (byteOrientation != HORIZONTAL && byteOrientation != VERTICAL) {
            throw new IllegalArgumentException("Argument orientation only can be HORIZONTAL or VERTICAL");
        }
        short[][] arr;
        if (byteOrientation == HORIZONTAL) {
            arr = new short[image.getHeight()][];
        } else {
            int size = image.getHeight() % ONE_BYTE == 0 ?
                    image.getHeight() / ONE_BYTE :
                    image.getHeight() / ONE_BYTE + 1;
            arr = new short[size][];
        }
        for (int y = 0; y < image.getHeight(); ) {
            for (int x = 0; x < image.getWidth(); ) {
                if (byteOrientation == HORIZONTAL) {
                    short one = toHorizontalShort(image, x, y);
                    if (arr[y] == null) {
                        arr[y] = new short[image.getWidth() % ONE_BYTE == 0 ?
                                image.getWidth() / ONE_BYTE :
                                image.getWidth() / ONE_BYTE + 1];
                    }
                    arr[y][x / ONE_BYTE] = one;
                    x += ONE_BYTE;
                } else {
                    short one = toVerticalShort(image, x, y);
                    if (arr[y / ONE_BYTE] == null) {
                        arr[y / ONE_BYTE] = new short[image.getWidth()];
                    }
                    arr[y / ONE_BYTE][x] = one;
                    x++;
                }
            }
            if (byteOrientation == HORIZONTAL) {
                y++;
            } else {
                y += ONE_BYTE;
            }
        }
        return arr;
    }

    private static short toVerticalShort(BufferedImage image, int x, int y) {
        short data = 0;
        int count = 0;
        for (int i = 0; i < ONE_BYTE && y + i < image.getHeight(); i++) {
            int rgb = image.getRGB(x, y + i);
            if (isBlack(rgb)) {
                data |= 0x01;
            } else {
                data |= 0;
            }
            if (i < ONE_BYTE - 1) {
                data <<= 1;
            }
            count++;
        }
        data <<= (ONE_BYTE - count);
        return data;
    }


    private static short toHorizontalShort(BufferedImage image, int x, int y) {
        short data = 0;
        int count = 0;
        for (int i = 0; i < ONE_BYTE && x + i < image.getWidth(); i++) {
            int rgb = image.getRGB(x + i, y);
            if (isBlack(rgb)) {
                data |= 1;
            } else {
                data |= 0;
            }
            if (i < ONE_BYTE - 1) {
                data <<= 1;
            }
            count++;
        }
        data <<= (ONE_BYTE - count);
        return data;
    }

    private static boolean isBlack(int rgb) {
        Color color = new Color(rgb);
        return color.getRed() < 50 || color.getGreen() < 50 || color.getBlue() < 50;
    }

    /**
     * 将一行点阵数据转换为可见字符
     * 0：+
     * 1：E
     *
     * @param arr      数据
     * @param division 是否分割显示每个字节 为true时使用“|”分割字节
     * @return 可见字符
     */
    public static String toBitLine(short[] arr, boolean division) {
        StringBuilder sb = new StringBuilder();
        for (short s : arr) {
            String bit = Integer.toBinaryString(s);
            if (bit.length() < 8) {
                bit = "00000000" + bit;
                bit = bit.substring(bit.length() - 8, bit.length());
            }
            sb.append(bit.replaceAll("0", "+").replaceAll("1", "E"));
            if (division) {
                sb.append("|");
            }
        }
        return sb.toString();
    }
}